//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability.Adm;
using EntLibContrib.Resource.Properties;

namespace EntLibContrib.Resource.Configuration.Manageability
{
	/// <summary>
	/// <para>This type supports the Enterprise Library Manageability Extensions infrastructure and is not intended to 
	/// be used directly from your code.</para>
	/// Represents the behaviour required to provide Group Policy updates and to publish the <see cref="ConfigurationSetting"/> 
	/// instances associated to the configuration information for the Resource Application Block, and it also manages
	/// the creation of the ADM template categories and policies required to edit Group Policy Objects for the block.
	/// </summary>
	/// <remarks>
	/// This class performs the actual Group Policy update and Wmi object generation for the <see cref="ResourceSettings"/>
	/// configuration section and the <see cref="ResourceManagerData"/> instances contained by it. Processing for 
	/// <see cref="ResourceProviderData"/> instances is delegated to 
	/// <see cref="ConfigurationElementManageabilityProvider"/> objects registered to the configuration object data types.
	/// <para>
	/// The Group Policy directives for the Resource Application Block differ from other block's directives in that policies are
	/// only generated for resource managers, and these policies contain the parts used to override the settings for the
	/// ResourceProviderData instance identified by the <see cref="ResourceManagerData.ResourceProvider"/> 
	/// property instance, if any. Manageability providers registered for ResourceProviderData 
	/// subclasses must not generate policies, and the parts they generate must include the
	/// corresponding key name, as they will be included in the resource managers' policies. The purpose for this policy structure
	/// is to make the experience of editing a Group Policy Object's policies for the Resource Application Block similar to that
	/// of the Enterprise Library Configuration Console.
	/// </para>
	/// </remarks>
	/// <seealso cref="ConfigurationSectionManageabilityProvider"/>
	/// <seealso cref="ConfigurationElementManageabilityProvider"/>
	public sealed class ResourceSettingsManageabilityProvider
		: ConfigurationSectionManageabilityProviderBase<ResourceSettings>
	{
		#region Constants
		/// <summary>
		/// The default resource manager property name
		/// </summary>
		public const string DefaultResourceManagerPropertyName = "defaultResourceManager";
		/// <summary>
		/// The resource managers key name
		/// </summary>
		public const string ResourceManagersKeyName = "resourceManagers";
		/// <summary>
		/// The resource providers key name
		/// </summary>
		public const string ResourceProvidersKeyName = "resourceProviders";
		#endregion

		#region Fields
		readonly CustomResourceManagerDataManageabilityProvider customManageabilityProvider = new CustomResourceManagerDataManageabilityProvider();
		#endregion

		#region Properties
		/// <summary>
		/// Gets the name of the category that represents the whole configuration section.
		/// </summary>
		protected override string SectionCategoryName
		{
			get { return InternalResources.ResourceSectionCategoryName; }
		}

		/// <summary>
		/// Gets the name of the managed configuration section.
		/// </summary>
		protected override string SectionName
		{
			get { return ResourceSettings.SectionName; }
		}
		#endregion

		#region Construction
		/// <summary>
		/// <para>This method supports the Enterprise Library Manageability Extensions infrastructure and is not intended to 
		/// be used directly from your code.
		/// </para>
		/// Initializes a new instance of the <see cref="ResourceSettingsManageabilityProvider"/> class with a 
		/// given set of manageability providers to use when dealing with the configuration for resource providers.
		/// </summary>
		/// <param name="subProviders">The mapping from configuration element type to
		/// <see cref="ConfigurationElementManageabilityProvider"/>.</param>
		public ResourceSettingsManageabilityProvider(IDictionary<Type, ConfigurationElementManageabilityProvider> subProviders)
			: base(subProviders)
		{
			ResourceSettingsWmiMapper.RegisterWmiTypes();
		}
		#endregion

		#region ADM Generation
		/// <summary>
		/// Adds the ADM instructions that describe the policies that can be used to override the configuration
		/// information represented by a configuration section.
		/// </summary>
		/// <param name="contentBuilder">The <see cref="T:AdmContentBuilder"/> to which the Adm instructions are to be appended.</param>
		/// <param name="configurationSection">The configuration section instance.</param>
		/// <param name="configurationSource">The configuration source from where to get additional configuration
		/// information, if necessary.</param>
		/// <param name="sectionKey">The root key for the section's policies.</param>
		protected override void AddAdministrativeTemplateDirectives(AdmContentBuilder contentBuilder,
																																ResourceSettings configurationSection,
																																IConfigurationSource configurationSource,
																																string sectionKey)
		{
			AddBlockSettingsPolicy(contentBuilder, configurationSection, sectionKey);
			AddResourceManagersPolicies(contentBuilder,
																	configurationSection,
																	configurationSource,
																	sectionKey,
																	InternalResources.ResourceManagersCategoryName);
		}

		/// <summary>
		/// Adds the block settings policy.
		/// </summary>
		/// <param name="contentBuilder">The content builder.</param>
		/// <param name="configurationSection">The configuration section.</param>
		/// <param name="sectionKey">The section key.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
		private void AddBlockSettingsPolicy(AdmContentBuilder contentBuilder,
																															ResourceSettings configurationSection,
																															string sectionKey)
		{
			contentBuilder.StartPolicy(InternalResources.ResourceSettingsPolicyName, sectionKey);
			{
				contentBuilder.AddDropDownListPartForNamedElementCollection(InternalResources.ResourceSettingsDefaultResourceManagerPartName,
																																		DefaultResourceManagerPropertyName,
																																		configurationSection.ResourceManagers,
																																		configurationSection.DefaultResourceManager,
																																		false);
			}
			contentBuilder.EndPolicy();
		}

		/// <summary>
		/// Adds the resource managers policies.
		/// </summary>
		/// <param name="contentBuilder">The content builder.</param>
		/// <param name="configurationSection">The configuration section.</param>
		/// <param name="configurationSource">The configuration source.</param>
		/// <param name="sectionKey">The section key.</param>
		/// <param name="categoryName">Name of the category.</param>
		/// <devdoc>
		/// ADM templates for resources are different from the other blocks' templates to match the configuration console's
		/// user experience. Instead of having separate categories with policies for resource managers and resource providers,
		/// the policy for a resource manager includes the parts for its resource provider.
		/// </devdoc>
		private void AddResourceManagersPolicies(AdmContentBuilder contentBuilder,
																						ResourceSettings configurationSection,
																						IConfigurationSource configurationSource,
																						string sectionKey,
																						string categoryName)
		{
			string resourceManagersKey = sectionKey + @"\" + ResourceManagersKeyName;
			string resourceProvidersKey = sectionKey + @"\" + ResourceProvidersKeyName;

			contentBuilder.StartCategory(categoryName);

			foreach (ResourceManagerDataBase resourceManagerDataBase in configurationSection.ResourceManagers)
			{
				if (resourceManagerDataBase is CustomResourceManagerData)
				{
					customManageabilityProvider.AddAdministrativeTemplateDirectives(contentBuilder,
																																					resourceManagerDataBase,
																																					configurationSource,
																																					resourceManagersKey);
				}
				else
				{
					string resourceManagerPolicyKey = resourceManagersKey + @"\" + resourceManagerDataBase.Name;

					contentBuilder.StartPolicy(String.Format(CultureInfo.InvariantCulture,
																									InternalResources.ResourceManagerPolicyNameTemplate,
																									resourceManagerDataBase.Name),
																		resourceManagerPolicyKey);

					ResourceManagerData resourceManagerData = resourceManagerDataBase as ResourceManagerData;
					if (resourceManagerData != null)
					{

						// append the resource manager's provider parts
						contentBuilder.AddTextPart(InternalResources.ResourceProviderSettingsPartName);
						ResourceProviderDataBase resourceProviderData = configurationSection.ResourceProviders.Get(resourceManagerData.ResourceProvider);
						ConfigurationElementManageabilityProvider resourceProviderDataManageabilityProvider = GetSubProvider(resourceProviderData.GetType());
						AddAdministrativeTemplateDirectivesForElement(contentBuilder,
																													resourceProviderData,
																													resourceProviderDataManageabilityProvider,
																													configurationSource,
																													resourceProvidersKey);
					}
					contentBuilder.EndPolicy();
				}
			}
			contentBuilder.EndCategory();
		}
		#endregion

		#region Manageability Support
		/// <summary>
		/// Creates the <see cref="ConfigurationSetting"/> instances that describe the <paramref name="configurationSection"/>.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="wmiSettings">A collection to where the generated WMI objects are to be added.</param>
		protected override void GenerateWmiObjectsForConfigurationSection(ResourceSettings configurationSection, ICollection<ConfigurationSetting> wmiSettings)
		{
			ResourceSettingsWmiMapper.GenerateWmiObjects(configurationSection, wmiSettings);
		}

		/// <summary>
		/// Overrides the <paramref name="configurationSection"/>'s properties with the Group Policy values from 
		/// the registry.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="policyKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides.</param>
		protected override void OverrideWithGroupPoliciesForConfigurationSection(ResourceSettings configurationSection, IRegistryKey policyKey)
		{
			string defaultResourceManagerOverride = policyKey.GetStringValue(DefaultResourceManagerPropertyName);

			configurationSection.DefaultResourceManager = defaultResourceManagerOverride;
		}

		/// <summary>
		/// Overrides the <paramref name="configurationSection"/>'s configuration elements' properties
		/// with the Group Policy values from the registry, if any, and creates the <see cref="ConfigurationSetting"/>
		/// instances that describe these configuration elements.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="readGroupPolicies"><see langword="true"/> if Group Policy overrides must be applied; otherwise,
		/// <see langword="false"/>.</param>
		/// <param name="machineKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides for the
		/// configuration section at the machine level, or <see langword="null"/>
		/// if there is no such registry key.</param>
		/// <param name="userKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides for the
		/// configuration section at the user level, or <see langword="null"/>
		/// if there is no such registry key.</param>
		/// <param name="generateWmiObjects"><see langword="true"/> if WMI objects must be generated; otherwise,
		/// <see langword="false"/>.</param>
		/// <param name="wmiSettings">A collection to where the generated WMI objects are to be added.</param>
		/// <remarks>Errors detected while processing policy overrides for the configuration elements in the section
		/// must be logged but processing for other objects must not be interrupted.</remarks>
		protected override void OverrideWithGroupPoliciesAndGenerateWmiObjectsForConfigurationElements(ResourceSettings configurationSection,
																																																	bool readGroupPolicies,
																																																	IRegistryKey machineKey,
																																																	IRegistryKey userKey,
																																																	bool generateWmiObjects,
																																																	ICollection<ConfigurationSetting> wmiSettings)
		{
			OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManagers(configurationSection.ResourceManagers,
																																				readGroupPolicies,
																																				machineKey,
																																				userKey,
																																				generateWmiObjects,
																																				wmiSettings);
			OverrideWithGroupPoliciesAndGenerateWmiObjectsForElementCollection(configurationSection.ResourceProviders,
																																				ResourceProvidersKeyName,
																																				readGroupPolicies,
																																				machineKey,
																																				userKey,
																																				generateWmiObjects,
																																				wmiSettings);
		}

		/// <summary>
		/// Overrides the with group policies and generate WMI objects for resource managers.
		/// </summary>
		/// <param name="resourceManagers">The resource managers.</param>
		/// <param name="readGroupPolicies">if set to <c>true</c> [read group policies].</param>
		/// <param name="machineKey">The machine key.</param>
		/// <param name="userKey">The user key.</param>
		/// <param name="generateWmiObjects">if set to <c>true</c> [generate WMI objects].</param>
		/// <param name="wmiSettings">The WMI settings.</param>
		void OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManagers(NamedElementCollection<ResourceManagerDataBase> resourceManagers,
																																					bool readGroupPolicies,
																																					IRegistryKey machineKey,
																																					IRegistryKey userKey,
																																					bool generateWmiObjects,
																																					ICollection<ConfigurationSetting> wmiSettings)
		{
			List<ResourceManagerDataBase> elementsToRemove = new List<ResourceManagerDataBase>();

			IRegistryKey machineResourceManagersKey = null;
			IRegistryKey userResourceManagersKey = null;

			try
			{
				LoadRegistrySubKeys(ResourceManagersKeyName,
														machineKey,
														userKey,
														out machineResourceManagersKey,
														out userResourceManagersKey);

				foreach (ResourceManagerDataBase data in resourceManagers)
				{
					IRegistryKey machineResourceManagerKey = null;
					IRegistryKey userResourceManagerKey = null;

					try
					{
						LoadRegistrySubKeys(data.Name,
																machineResourceManagersKey,
																userResourceManagersKey,
																out machineResourceManagerKey,
																out userResourceManagerKey);

						if (data is CustomResourceManagerData)
						{
							if (!customManageabilityProvider.OverrideWithGroupPoliciesAndGenerateWmiObjects(data,
																																															readGroupPolicies,
																																															machineResourceManagerKey,
																																															userResourceManagerKey,
																																															generateWmiObjects,
																																															wmiSettings))
							{
								elementsToRemove.Add(data);
							}
						}
						else
						{
							if (!OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManager(data,
																																										readGroupPolicies,
																																										machineResourceManagerKey,
																																										userResourceManagerKey,
																																										generateWmiObjects,
																																										wmiSettings))
							{
								elementsToRemove.Add(data);
							}
						}
					}
					finally
					{
						ReleaseRegistryKeys(machineResourceManagerKey, userResourceManagerKey);
					}
				}
			}
			finally
			{
				ReleaseRegistryKeys(machineResourceManagersKey, userResourceManagersKey);
			}

			foreach (ResourceManagerDataBase data in elementsToRemove)
			{
				resourceManagers.Remove(data.Name);
			}
		}

		/// <summary>
		/// Overrides the with group policies and generate WMI objects for resource manager.
		/// </summary>
		/// <param name="resourceManagerDataBase">The resource manager data base.</param>
		/// <param name="readGroupPolicies">if set to <c>true</c> [read group policies].</param>
		/// <param name="machineKey">The machine key.</param>
		/// <param name="userKey">The user key.</param>
		/// <param name="generateWmiObjects">if set to <c>true</c> [generate WMI objects].</param>
		/// <param name="wmiSettings">The WMI settings.</param>
		/// <returns></returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
		bool OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManager(ResourceManagerDataBase resourceManagerDataBase,
																																					bool readGroupPolicies,
																																					IRegistryKey machineKey,
																																					IRegistryKey userKey,
																																					bool generateWmiObjects,
																																					ICollection<ConfigurationSetting> wmiSettings)
		{
			if (readGroupPolicies)
			{
				IRegistryKey policyKey = machineKey != null ? machineKey : userKey;
				if (policyKey != null)
				{
					if (policyKey.IsPolicyKey && !policyKey.GetBoolValue(PolicyValueName).Value)
					{
						return false;
					}
				}
			}
			if (generateWmiObjects)
			{
				ResourceSettingsWmiMapper.GenerateResourceManagerWmiObjects(resourceManagerDataBase, wmiSettings);
			}

			return true;
		}
		#endregion
	}
}
